package com.wanho.qz103.hrsaas.commons.dao.impl;

import com.wanho.qz103.hrsaas.commons.exception.DAOException;
import com.wanho.qz103.hrsaas.commons.exception.DataAccessException;
import com.wanho.qz103.hrsaas.commons.query.Page;
import com.wanho.qz103.hrsaas.commons.util.JDBCTemplate;
import com.wanho.qz103.hrsaas.sys.pojos.Role;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 抽象所有 dao实现类共同代码
 * 使用：模板方法 设计模式
 */
@Slf4j
public abstract class BaseDAOImpl<T> {
    /** 使用 工具类 */
    protected JDBCTemplate jdbcTemplate = new JDBCTemplate() ;
    /**封装实体类*/
    private Class<?> pojosCls ;

    public BaseDAOImpl() {
        // 获得子类： 泛型中类类型  T = Role.class
        //动态获得子类的 泛型
        Type genType = this.getClass().getGenericSuperclass();
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        if (params==null || params.length==0){
            log.error("请检查您的子类是否传递泛型：例如：RoleDAOImpl extends BaseDAOImpl<Role>！！！");
        }
        this.pojosCls =  (Class)params[0];
    }

    /**
     * 依赖： 子类 提供 删除语句sql实现
     * @return
     */
    protected abstract String deleteSQL();

    /**
     * 依赖： 子类提供 新增SQL语句
     * @return
     */
    protected abstract String insertSQL() ;

    /**
     * 依赖： 子类 提供 修改 SQL语句
     * @return
     */
    protected abstract String updateSQL() ;

    /**
     * 依赖：子类 提供根据id查询SQL语句
     * @return
     */
    protected abstract String selectByIdSQL() ;
    /**
     * 依赖： 子类 提供 统计SQL语句  默认实现  子类可以不实现
     * @return
     */
    protected String selectCountSQL(){
        return null ;
    }

    /**
     * 依赖： 子类提供 limit 分页查询 当前页数据
     * @return
     */
    protected String selectTopNSQL(Page page){
        return null;
    }

    /**
     * 依赖： 子类提供 查询所有的SQL
     * @return
     */
    protected String selectAllSQL(){
        return null ;
    }

    /**
     *  使用正则表达式 替换 sql
     * @param propSQL  类似：insert into sys_role (id,name,region) values(#{id},#{name},#{region})
     * @return  jdbc使用sql: insert into sys_role (id,name,region) values(?,?,?)
     */
    private String replaceParam(String propSQL){
        return propSQL.replaceAll("#\\{[a-zA-Z0-9_.]+}", "?");
    }

    /**
     * 把子类的SQL #{xxx} 转换 Object[]中的具体的值
     * @param sql   insert into sys_role (id,name,region) values(#{id},#{name},#{region})
     * @param obj   Role.builder().name("大将军").region("统领三军").id("DJJ")
     * @return    new Object[]{"DJJ","大将军","统领三军"}
     */
    private Object[] getParamAy(String sql,Object obj){
        //参数值  占位符的值
        List<Object> paramList = new ArrayList<>() ;
        //使用 字符串进行分割   #{  结果:  insert into sys_role (id,name,region) values(   id},  name}, region})
        String[] rs2 = sql.split("#\\{");
        // 跳过 第一个元素   映射 截取 开始到}【不包括】  去空格  收集  List    [id,name,region]
        List<String> list = Stream.of(rs2).skip(1).map(s -> s.substring(0, s.indexOf("}")).trim()).collect(Collectors.toList());
        //循环 属性列名 集合
        list.forEach(propName->{
            try {
                //调用 apache 反射jar  实现 根据属性名 直接获得属性值
                Field declaredField = obj.getClass().getDeclaredField(propName);
                //设置该属性可被访问
                declaredField.setAccessible(true);
                //调用 访问： 属性值
                Object propValue = declaredField.get(obj);
                //属性值 添加到 集合中
                paramList.add(propValue) ;
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        // 对象数组
        return paramList.toArray() ;
    }

    public boolean insert(T pojos) throws DAOException {
        try {
            String insertSQL = this.insertSQL();
            String DML = this.replaceParam(insertSQL) ;
            Object[] paramAy = this.getParamAy(insertSQL,pojos) ;
            return jdbcTemplate.update(DML,paramAy);
        } catch (DataAccessException e) {
            e.printStackTrace();
            log.error(e.getMessage());
            throw new DAOException("新增数据["+pojos+"]异常！！！") ;
        }
    }


    public boolean update(T pojos) throws DAOException {
        try {
            String updateSQL = this.updateSQL();
            String DML = this.replaceParam(updateSQL) ;
            Object[] paramAy = this.getParamAy(updateSQL,pojos);
            return jdbcTemplate.update(DML,paramAy);
        } catch (DataAccessException e) {
            e.printStackTrace();
            log.error(e.getMessage());
            throw new DAOException("修改数据["+pojos+"]异常！！！") ;
        }
    }


    public boolean delete(String id) throws DAOException {
        try {
            String deleteSQL = this.deleteSQL();
            String DML = this.replaceParam(deleteSQL) ;
            Object[] paramAy = {id} ;
            return jdbcTemplate.update(DML,paramAy);
        } catch (DataAccessException e) {
            e.printStackTrace();
            log.error(e.getMessage());
            throw new DAOException("删除数据["+id+"]异常！！！") ;
        }
    }

    public long selectCount() throws DAOException {
        try {
            String sql = this.selectCountSQL();
            Object[] paramAy = {} ;
            return jdbcTemplate.queryLong(sql,paramAy);
        } catch (DataAccessException e) {
            e.printStackTrace();
            log.error(e.getMessage());
            throw new DAOException("统计数据异常！！！") ;
        }
    }


    public List<T> selectTopN(Page page) throws DAOException {
        try {
            String selectTopNSQL = this.selectTopNSQL(page);
            String sql = this.replaceParam(selectTopNSQL) ;
            Object[] paramAy = this.getParamAy(selectTopNSQL,page);
            return jdbcTemplate.queryList(sql,pojosCls,paramAy);
        } catch (DataAccessException e) {
            e.printStackTrace();
            log.error(e.getMessage());
            throw new DAOException("分页查询数据异常！！！") ;
        }
    }


    public T selectById(String id) throws DAOException {
        try {
            String selectByIdSQL = this.selectByIdSQL();
            String sql = this.replaceParam(selectByIdSQL) ;
            Object[] paramAy = {id} ;
            return jdbcTemplate.queryObject(sql,pojosCls,paramAy);
        } catch (DataAccessException e) {
            e.printStackTrace();
            log.error(e.getMessage());
            throw new DAOException("统计数据异常！！！") ;
        }
    }

    public List<T> selectAll() throws DAOException{
        try {
            String sql = this.selectAllSQL() ;
            Object[] paramAy = {} ;
            return jdbcTemplate.queryList(sql,pojosCls,paramAy);
        } catch (DataAccessException e) {
            e.printStackTrace();
            log.error(e.getMessage());
            throw new DAOException("查询所有数据异常！！！") ;
        }
    }
}
